package models

import (
	"fmt"
	"github.com/go-xorm/xorm"
	_ "github.com/mattn/go-sqlite3"
	"github.com/pkg/errors"
	"log"
	"os"
)

type Account struct {
	Id      int64
	Name    string `xorm:"unique"`
	Balance float64
	Version int `xorm:"version"`
}

//before insert事件钩子
func (a Account) BeforeInsert() {
	fmt.Printf("BeforeInsert name:%s\n", a.Name)
}

//after insert事件钩子
func (a Account) AfterInsert() {
	fmt.Printf("AfterInsert name:%s\n", a.Name)
}

var x *xorm.Engine

func init() {
	var err error
	x, err = xorm.NewEngine("sqlite3", "./bank.db")
	if err != nil {
		log.Fatalf("Fail to create engine: %v", err)
	}

	if err = x.Sync(new(Account)); err != nil {
		log.Fatalf("Fail to sync database %v", err)
	}

	f, err := os.Create("./sql.log")
	if err != nil {
		log.Fatalf("Fail to create log file: %v\n", err)
	}

	//开启sql日志
	x.SetLogger(xorm.NewSimpleLogger(f))
	x.ShowSQL(true)

	//开始LRU缓存
	cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
	x.SetDefaultCacher(cacher)
}
func NewAccount(name string, balance float64) error {
	//before after 事件钩子
	//before := func(bean interface{}) {
	//	fmt.Println("before", bean)
	//}
	//after := func(bean interface{}) {
	//	fmt.Println("after", bean)
	//}
	//_, err := x.Before(before).After(after).Insert(Account{Name: name, Balance: balance})
	_, err := x.Insert(Account{Name: name, Balance: balance})
	return err
}

func GetAccount(id int64) (*Account, error) {
	a := &Account{}
	has, err := x.ID(id).Get(a)
	if err != nil {
		return nil, err
	} else if !has {
		return nil, errors.New("Account not found")
	}
	return a, nil
}

func MakeDeposit(id int64, deposit float64) (*Account, error) {
	a, err := GetAccount(id)
	if err != nil {
		return nil, err
	}

	a.Balance += deposit
	_, err = x.Update(a)
	return a, err
}

func MakeWithdraw(id int64, withdraw float64) (*Account, error) {
	a, err := GetAccount(id)
	if err != nil {
		return nil, err
	}

	if a.Balance < withdraw {
		return nil, errors.New("Not enough balance")
	}

	a.Balance -= withdraw
	_, err = x.Update(a)
	return a, err

}

func MakeTransfer(id1, id2 int64, balance float64) error {
	a1, err := GetAccount(id1)
	if err != nil {
		return err
	}
	a2, err := GetAccount(id2)
	if err != nil {
		return err
	}

	if a1.Balance <= balance {
		return errors.New("Not enought balance")
	}

	a1.Balance -= balance
	a2.Balance += balance

	if _, err := x.Update(a1); err != nil {
		return err
	} else if _, err := x.Update(a2); err != nil {
		return err
	}

	return nil
}
func MakeTransferTransation(id1, id2 int64, balance float64) error {
	a1, err := GetAccount(id1)
	if err != nil {
		return err
	}
	a2, err := GetAccount(id2)
	if err != nil {
		return err
	}

	if a1.Balance <= balance {
		return errors.New("Not enought balance")
	}

	a1.Balance -= balance
	a2.Balance += balance
	sess := x.NewSession()
	defer sess.Close()
	if err := sess.Begin(); err != nil {
		return err
	}

	if _, err := sess.Update(a1); err != nil {
		sess.Rollback()
		return err
	} else if _, err := sess.Update(a2); err != nil {
		sess.Rollback()
		return err
	}

	return sess.Commit()
}

func GetAccoutAscId() (a []*Account, err error) {
	err = x.Asc("id").Find(&a)
	return
}
func GetAccoutAscBalance() (a []*Account, err error) {
	err = x.Asc("balance").Find(&a)
	return
}

func DeleteAccount(id int64) error {
	_, err := x.Delete(&Account{Id: id})
	return err
}
func GetAccountCount() (int64, error) {
	c, err := x.Count(new(Account))
	if err != nil {
		return c, err
	}

	return c, nil
}

var xprintln = func(idx int, bean interface{}) error {
	fmt.Printf("%d:%#v\n", idx, bean.(*Account))
	return nil
}

func IteratorPrintln() {
	x.Iterate(new(Account), xprintln)
}

func RowPrintln() {
	rows, err := x.Rows(new(Account))
	defer rows.Close()
	if err != nil {
		fmt.Println(err)
	}
	a := new(Account)
	i := 0
	for rows.Next() {
		rows.Scan(a)
		fmt.Printf("%d:%#v\n", i, a)
		i++
	}
}

func ColsNamePrintln() {
	x.Cols("name").Iterate(new(Account), xprintln)
}

func OmitNamePrintln() {
	x.Omit("name").Iterate(new(Account), xprintln)
}
func LimitPrintln() {
	x.Limit(3, 2).Iterate(new(Account), xprintln)
}
